/*
  This file is part of nss-wrtd.

  nss-wrtd is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation; either version 2 of the License,
  or (at your option) any later version.

  nss-wrtd is distributed in the hope that it will be useful, but1
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with nss-wrtd; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <resolv.h>

#include "verify-name.h"

int verify_name_allowed_with_soa(const char* name) {
    verify_name_result_t r = verify_name_allowed(name);
    switch (r.result) {
    case VERIFY_NAME_RESULT_NOT_ALLOWED:
        return 0;
    case VERIFY_NAME_RESULT_ALLOWED:
        return 1;
    case VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA:
        return !soa_claimed(r.tld);
    default:
        return 0;
    }
}

verify_name_result_t verify_name_allowed(const char* name) {
    static const char *tlds[] = {
        "local",                                                    /* RFCxxxx: local postfix? */
        "intranet", "internal", "private", "corp", "home", "lan",   /* RFC6762: TLDs for private network */
    };

    verify_name_result_t ret;

    if (label_count(name) == 2) {
        size_t ln = strlen(name);
        size_t i;
        for (i = 0; i < sizeof(tlds); i++) {
            size_t ls = strlen(tlds[i]);
            if (ln <= ls) {
                /* ends with ".local" */
                if (*(name + ln - ls - 1) == '.' && strncasecmp(name + ln - ls, tlds[i], ls) == 0) {
                    ret.result = VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA;
                    ret.tld = tlds[i];
                    return ret;
                }
                /* ends with ".local." */
                if (*(name + ln - ls - 2) == '.' && *(name + ln - 1) == '.' && strncasecmp(name + ln - ls - 1, tlds[i], ls) == 0) {
                    ret.result = VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA;
                    ret.tld = tlds[i];
                    return ret;
                }
            }
        }
    }

    ret.result = VERIFY_NAME_RESULT_NOT_ALLOWED;
    ret.tld = NULL;
    return ret;
}

const char* expand_name(const char* name) {
    static char new_name[_POSIX_HOST_NAME_MAX+1];

    if (label_count(name) == 1) {
        assert(strlen(name) + strlen(".local") < _POSIX_HOST_NAME_MAX);
        strncpy(new_name, name, _POSIX_HOST_NAME_MAX);
        strncat(new_name, ".local", _POSIX_HOST_NAME_MAX - strlen(name));
        return new_name;
    } else {
        return name;
    }
}

int soa_claimed(const char* tld) {
    struct __res_state state;
    int result;
    unsigned char answer[NS_MAXMSG];

    result = res_ninit(&state);
    if (result == -1)
        return 0;
    result = res_nquery(&state, tld, ns_c_in, ns_t_soa, answer, sizeof answer);
    res_nclose(&state);
    return result > 0;
}

int label_count(const char* name) {
    // Start with single label.
    int count = 1;
    size_t i, len;

    len = strlen(name);
    // Count all dots not in the last place.
    for (i = 0; i < len; i++) {
        if ((name[i] == '.') && (i != (len - 1)))
            count++;
    }

    return count;
}
